msg_tool\scripts\yuris/
ystd.rs

1//! Yu-Ris YSTD(Global counts) file (.ybn)
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use serde::{Deserialize, Serialize};
10use std::io::{Read, Seek, Write};
11
12#[derive(Debug, StructUnpack, StructPack, Serialize, Deserialize)]
13struct YSTDData {
14    version: u32,
15    num_variables: u32,
16    num_texts: u32,
17}
18
19#[derive(Debug)]
20pub struct YSTDBuilder {}
21
22impl YSTDBuilder {
23    /// Creates a new instance of `YSTDBuilder`
24    pub const fn new() -> Self {
25        YSTDBuilder {}
26    }
27}
28
29impl ScriptBuilder for YSTDBuilder {
30    fn default_encoding(&self) -> Encoding {
31        Encoding::Cp932
32    }
33
34    fn build_script(
35        &self,
36        buf: Vec<u8>,
37        _filename: &str,
38        encoding: Encoding,
39        _archive_encoding: Encoding,
40        config: &ExtraConfig,
41        _archive: Option<&Box<dyn Script>>,
42    ) -> Result<Box<dyn Script + Send + Sync>> {
43        Ok(Box::new(YSTD::new(MemReader::new(buf), encoding, config)?))
44    }
45
46    fn extensions(&self) -> &'static [&'static str] {
47        &["ybn"]
48    }
49
50    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
51        if buf_len >= 4 && buf.starts_with(b"YSTD") {
52            return Some(20);
53        }
54        None
55    }
56
57    fn script_type(&self) -> &'static ScriptType {
58        &ScriptType::YurisYSTD
59    }
60
61    fn can_create_file(&self) -> bool {
62        true
63    }
64
65    fn create_file<'a>(
66        &'a self,
67        filename: &'a str,
68        writer: Box<dyn WriteSeek + 'a>,
69        encoding: Encoding,
70        file_encoding: Encoding,
71        config: &ExtraConfig,
72    ) -> Result<()> {
73        create_file(
74            filename,
75            writer,
76            encoding,
77            file_encoding,
78            config.custom_yaml,
79        )
80    }
81}
82
83#[derive(Debug)]
84pub struct YSTD {
85    data: YSTDData,
86    custom_yaml: bool,
87}
88
89impl YSTD {
90    pub fn new<T: Read + Seek>(
91        mut reader: T,
92        encoding: Encoding,
93        config: &ExtraConfig,
94    ) -> Result<Self> {
95        let mut sig = [0; 4];
96        reader.read_exact(&mut sig)?;
97        if &sig != b"YSTD" {
98            anyhow::bail!("Unsupported YSTD file.");
99        }
100        let data = YSTDData::unpack(&mut reader, false, encoding, &None)?;
101        Ok(Self {
102            data,
103            custom_yaml: config.custom_yaml,
104        })
105    }
106}
107
108impl Script for YSTD {
109    fn default_output_script_type(&self) -> OutputScriptType {
110        OutputScriptType::Custom
111    }
112
113    fn is_output_supported(&self, output: OutputScriptType) -> bool {
114        matches!(output, OutputScriptType::Custom)
115    }
116
117    fn default_format_type(&self) -> FormatOptions {
118        FormatOptions::None
119    }
120
121    fn custom_output_extension(&self) -> &'static str {
122        if self.custom_yaml { "yaml" } else { "json" }
123    }
124
125    fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
126        let s = if self.custom_yaml {
127            serde_yaml_ng::to_string(&self.data)
128                .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
129        } else {
130            serde_json::to_string_pretty(&self.data)
131                .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
132        };
133        let mut writer = crate::utils::files::write_file(filename)?;
134        let s = encode_string(encoding, &s, false)?;
135        writer.write_all(&s)?;
136        writer.flush()?;
137        Ok(())
138    }
139
140    fn custom_import<'a>(
141        &'a self,
142        custom_filename: &'a str,
143        file: Box<dyn WriteSeek + 'a>,
144        encoding: Encoding,
145        output_encoding: Encoding,
146    ) -> Result<()> {
147        create_file(
148            custom_filename,
149            file,
150            encoding,
151            output_encoding,
152            self.custom_yaml,
153        )
154    }
155}
156
157fn create_file<'a>(
158    custom_filename: &'a str,
159    mut writer: Box<dyn WriteSeek + 'a>,
160    encoding: Encoding,
161    output_encoding: Encoding,
162    yaml: bool,
163) -> Result<()> {
164    let input = crate::utils::files::read_file(custom_filename)?;
165    let s = decode_to_string(output_encoding, &input, true)?;
166    let data: YSTDData = if yaml {
167        serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
168    } else {
169        serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
170    };
171    writer.write_all(b"YSTD")?;
172    data.pack(&mut writer, false, encoding, &None)?;
173    Ok(())
174}